import { useEffect } from 'react'
import { Controlled as ControlledCodeMirror } from 'react-codemirror2'
import CodeMirror from 'codemirror'
import 'codemirror/lib/codemirror.css'
import 'codemirror/theme/ayu-dark.css'
import 'codemirror/mode/xml/xml.js'
// 光标行代码高亮
import 'codemirror/addon/selection/active-line'
// 折叠代码
import 'codemirror/addon/fold/foldgutter.css'
import 'codemirror/addon/fold/foldcode.js'
import 'codemirror/addon/fold/xml-fold.js'
import 'codemirror/addon/fold/foldgutter.js'
import 'codemirror/addon/fold/comment-fold.js'
// 代码提示补全和
import 'codemirror/addon/hint/xml-hint.js'
import 'codemirror/addon/hint/show-hint.css'
import './hint.css'
import 'codemirror/addon/hint/show-hint.js'
// 代码校验
import 'codemirror/addon/lint/lint'
import 'codemirror/addon/lint/lint.css'
import CodeMirrorRegisterXmlLint from './xml-lint'
// 输入> 时自动键入结束标签
import 'codemirror/addon/edit/closetag.js'
// 注释
import 'codemirror/addon/comment/comment.js'

// 用于调整codeMirror的主题样式
import style from './index.less'

// 注册Xml代码校验
CodeMirrorRegisterXmlLint(CodeMirror)

// 格式化相关
CodeMirror.extendMode("xml", {
  commentStart: "<!--",
  commentEnd: "-->",
  newlineAfterToken: function (type, content, textAfter, state) {
    return (type === "tag" && />$/.test(content) && state.context) ||
      /^</.test(textAfter);
  }
});

// 格式化指定范围
CodeMirror.defineExtension("autoFormatRange", function (from, to) {
  var cm = this;
  var outer = cm.getMode(), text = cm.getRange(from, to).split("\n");
  var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state);
  var tabSize = cm.getOption("tabSize");

  var out = "", lines = 0, atSol = from.ch === 0;
  function newline() {
    out += "\n";
    atSol = true;
    ++lines;
  }

  for (var i = 0; i < text.length; ++i) {
    var stream = new CodeMirror.StringStream(text[i], tabSize);
    while (!stream.eol()) {
      var inner = CodeMirror.innerMode(outer, state);
      var style = outer.token(stream, state), cur = stream.current();
      stream.start = stream.pos;
      if (!atSol || /\S/.test(cur)) {
        out += cur;
        atSol = false;
      }
      if (!atSol && inner.mode.newlineAfterToken &&
        inner.mode.newlineAfterToken(style, cur, stream.string.slice(stream.pos) || text[i + 1] || "", inner.state))
        newline();
    }
    if (!stream.pos && outer.blankLine) outer.blankLine(state);
    if (!atSol && i < text.length - 1) newline();
  }

  cm.operation(function () {
    cm.replaceRange(out, from, to);
    for (var cur = from.line + 1, end = from.line + lines; cur <= end; ++cur)
      cm.indentLine(cur, "smart");
    cm.setSelection(from, cm.getCursor(false));
  });
});

// Xml编辑器组件
function XmlEditor(props) {
  const { tags, value, onChange, onErrors, onGetEditor, onSave } = props

  useEffect(() => {
    // tags 每次变动时，都会重新改变校验规则
    CodeMirrorRegisterXmlLint(CodeMirror, tags, onErrors)
  }, [onErrors, tags])

  // 开始标签
  function completeAfter(cm, pred) {
    if (!pred || pred()) setTimeout(function () {
      if (!cm.state.completionActive)
        cm.showHint({
          completeSingle: false
        });
    }, 100);
    return CodeMirror.Pass;
  }

  // 结束标签
  function completeIfAfterLt(cm) {
    return completeAfter(cm, function () {
      var cur = cm.getCursor();
      return cm.getRange(CodeMirror.Pos(cur.line, cur.ch - 1), cur) === "<";
    });
  }

  // 属性和属性值
  function completeIfInTag(cm) {
    return completeAfter(cm, function () {
      var tok = cm.getTokenAt(cm.getCursor());
      if (tok.type === "string" && (!/['"]/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length === 1)) return false;
      var inner = CodeMirror.innerMode(cm.getMode(), tok.state).state;
      return inner.tagName;
    });
  }

  return (
    <div className={style.editor} >
      <ControlledCodeMirror
        value={value}
        options={{
          mode: {
            name: 'xml',
            // xml 属性换行的时候是否加上标签的长度
            multilineTagIndentPastTag: false
          },
          indentUnit: 2, // 换行的默认缩进多少个空格
          theme: 'ayu-dark', // 编辑器主题
          lineNumbers: true,// 是否显示行号
          autofocus: true,// 自动获取焦点
          styleActiveLine: true,// 光标行代码高亮
          autoCloseTags: true, // 在输入>时自动键入结束元素
          toggleComment: true, // 开启注释
          // 折叠代码 begin
          lineWrapping: true,
          foldGutter: true,
          gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
          // 折叠代码 end
          extraKeys: {
            // 代码提示
            "'<'": completeAfter,
            "'/'": completeIfAfterLt,
            "' '": completeIfInTag,
            "'='": completeIfInTag,
            // 注释功能
            "Ctrl-/": (cm) => {
              cm.toggleComment()
            },
            // 保存功能
            "Ctrl-S": (cm) => {
              onSave()
            },
            // 格式化
            "Shift-Alt-F": (cm) => {
              const totalLines = cm.lineCount();
              cm.autoFormatRange({ line: 0, ch: 0 }, { line: totalLines })
            },
            // Tab自动转换为空格
            "Tab": (cm) => {
              if (cm.somethingSelected()) {// 选中后整体缩进的情况
                cm.indentSelection('add')
              } else {
                cm.replaceSelection(Array(cm.getOption("indentUnit") + 1).join(" "), "end", "+input")
              }
            }
          },
          // 代码提示
          hintOptions: { schemaInfo: tags, matchInMiddle: true },
          lint: true
        }}
        editorDidMount={onGetEditor}
        onBeforeChange={onChange}
      />
    </div>
  )
}

export default XmlEditor
